package cn.ztuo.bitrade.runnable;

import cn.ztuo.bitrade.Trade.CoinTrader;
import cn.ztuo.bitrade.entity.*;
import cn.ztuo.bitrade.service.ContractOrderService;
import cn.ztuo.bitrade.service.ContractService;
import cn.ztuo.bitrade.util.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.math.BigDecimal;
import java.util.*;

@Slf4j
public class RobotOrderRunnable implements Runnable {

    public static Map<String, Integer> robotStatusMap = new HashMap<>();
    public static Map<String, Integer> robotErrorSum = new HashMap<>();
    public static Map<String, ContractOrder> lastPrice = new HashMap<>();
    private ContractOrderService orderService;
    private ContractCoin coin;
    private CoinTrader coinTrader;
    private ContractService contractService;
    public static Map<String, Map<ContractOrderDirection, List<ContractOrder>>> plateMap = new HashMap<>();

    public RobotOrderRunnable(ContractOrderService orderService, ContractCoin coin, CoinTrader coinTrader, ContractService contractService) {
        this.orderService = orderService;
        this.coin = coin;
        this.coinTrader = coinTrader;
        this.contractService = contractService;
    }

    private void robotAddOrder() {
        synchronized (coin) {
            try {
                log.info("合约机器人--" + coin.getSymbol() + "--下单中.......");
                if (robotStatusMap.get(coin.getSymbol()) == null || robotStatusMap.get(coin.getSymbol()) == 0) {
                    log.info(coin.getSymbol() + "=====机器人已经停止================");
                    coin.setRobotStatus(0);
                    Map<ContractOrderDirection, List<ContractOrder>> mapPlate = new HashMap<>();
                    if (!plateMap.isEmpty() && plateMap.containsKey(coin.getSymbol()) && plateMap.get(coin.getSymbol()) != null) {
                        mapPlate = plateMap.get(coin.getSymbol());
                        List<ContractOrder> bidsOld = mapPlate.get(ContractOrderDirection.BUY);
                        List<ContractOrder> asksOld = mapPlate.get(ContractOrderDirection.SELL);
                        coinTrader.cancelRobotOrder(bidsOld, ContractOrderDirection.BUY);
                        coinTrader.cancelRobotOrder(asksOld, ContractOrderDirection.SELL);
                    }
                    ContractUtils.getContractCoinService().saveAndFlush(coin);
                    return;
                }

                Ticker ticker = getTickerByUrl(coin);
                if (ticker == null) {
                    Thread.sleep(1 * 1000);
                    RobotOrderRunnable robotOrderRunnable = new RobotOrderRunnable(orderService, coin, coinTrader, contractService);
                    ContractService.executor.execute(robotOrderRunnable);
//                    contractService.robotAddOrder(coin);
                    return;
                }

                log.info("匹配当前 最新价格  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
                List<ContractOrder> bids = ticker.getBids();
                List<ContractOrder> asks = ticker.getAsks();
                Map<ContractOrderDirection, List<ContractOrder>> mapPlate = new HashMap<>();
                if (!plateMap.isEmpty() && plateMap.containsKey(coin.getSymbol()) && plateMap.get(coin.getSymbol()) != null) {
                    mapPlate = plateMap.get(coin.getSymbol());
                    List<ContractOrder> bidsOld = mapPlate.get(ContractOrderDirection.BUY);
                    List<ContractOrder> asksOld = mapPlate.get(ContractOrderDirection.SELL);
                    coinTrader.addRobotLimitPriceOrder(bids, ContractOrderDirection.BUY);
                    coinTrader.cancelRobotOrder(bidsOld, ContractOrderDirection.BUY);
                    coinTrader.addRobotLimitPriceOrder(asks, ContractOrderDirection.SELL);
                    coinTrader.cancelRobotOrder(asksOld, ContractOrderDirection.SELL);
                } else {
                    coinTrader.addRobotLimitPriceOrder(bids, ContractOrderDirection.BUY);
                    coinTrader.addRobotLimitPriceOrder(asks, ContractOrderDirection.SELL);
                }
                mapPlate.put(ContractOrderDirection.BUY, bids);
                mapPlate.put(ContractOrderDirection.SELL, asks);
                plateMap.put(coin.getSymbol(), mapPlate);

                List<ContractTrade> list = new ArrayList<>();
                ContractTrade trade = new ContractTrade();
                trade.setNowp(ticker.getTradeSize());
                trade.setAmount(ticker.getTradeSize().multiply(coin.getMinVolume()));
                trade.setPrice(ticker.getTradePrice());
                trade.setSymbol(coin.getSymbol());
                trade.setTime(Calendar.getInstance().getTimeInMillis());
                trade.setType(1);
                list.add(trade);
                log.info("trade ======================= {}", list);
                coinTrader.handlecontractTrade(list,1);


                ContractOrder bid = initRobotContractOrder(ContractOrderDirection.BUY, ticker);
                ContractOrder ask = initRobotContractOrder(ContractOrderDirection.SELL, ticker);
                lastPrice.put(coin.getSymbol() + "-bid", bid);
                lastPrice.put(coin.getSymbol() + "-ask", ask);
                log.info(coin.getSymbol() + "===开始匹配单=====================================================");
                coinTrader.trade(bid, orderService);
                coinTrader.trade(ask, orderService);
                Thread.sleep(1 * 1000);
                robotErrorSum.put(coin.getSymbol(), 0);

                RobotOrderRunnable robotOrderRunnable = new RobotOrderRunnable(orderService, coin, coinTrader, contractService);
                ContractService.executor.execute(robotOrderRunnable);
//                contractService.robotAddOrder(coin);

            } catch (Exception e) {
                e.printStackTrace();
                try {
                    if (robotErrorSum.get(coin.getSymbol()) == null) {
                        robotErrorSum.put(coin.getSymbol(), 0);
                    }
                    if (robotErrorSum.get(coin.getSymbol()) >= 5) {
                        log.error(coin.getSymbol() + "=====机器人已经停止================,error = {}", ExceptionUtil.getMessage(e));
                        Map<ContractOrderDirection, List<ContractOrder>> mapPlate = new HashMap<>();
                        if (!plateMap.isEmpty() && plateMap.containsKey(coin.getSymbol()) && plateMap.get(coin.getSymbol()) != null) {
                            mapPlate = plateMap.get(coin.getSymbol());
                            List<ContractOrder> bidsOld = mapPlate.get(ContractOrderDirection.BUY);
                            List<ContractOrder> asksOld = mapPlate.get(ContractOrderDirection.SELL);
                            coinTrader.cancelRobotOrder(bidsOld, ContractOrderDirection.BUY);
                            coinTrader.cancelRobotOrder(asksOld, ContractOrderDirection.SELL);
                        }
                        robotErrorSum.put(coin.getSymbol(), 0);
//                    ContractUtils.getContractCoinService().saveAndFlush(coin);
                    } else {
                        log.error(coin.getSymbol() + "=====机器人重启================,error = {}", ExceptionUtil.getMessage(e));
                        Thread.sleep(10 * 1000);
                        int count = robotErrorSum.get(coin.getSymbol()) == null ? 1 : robotErrorSum.get(coin.getSymbol()) + 1;
                        robotErrorSum.put(coin.getSymbol(), count);

                        RobotOrderRunnable robotOrderRunnable = new RobotOrderRunnable(orderService, coin, coinTrader, contractService);
                        ContractService.executor.execute(robotOrderRunnable);
//                    contractService.robotAddOrder(coin);
                    }
                } catch (InterruptedException ex) {
                    log.error(coin.getSymbol() + "=====机器人已经停止================,error = {}", ExceptionUtil.getMessage(ex));
                    robotStatusMap.put(coin.getSymbol(), 0);
                    robotErrorSum.put(coin.getSymbol(), 0);
                    Map<ContractOrderDirection, List<ContractOrder>> mapPlate = new HashMap<>();
                    if (!plateMap.isEmpty() && plateMap.containsKey(coin.getSymbol()) && plateMap.get(coin.getSymbol()) != null) {
                        mapPlate = plateMap.get(coin.getSymbol());
                        List<ContractOrder> bidsOld = mapPlate.get(ContractOrderDirection.BUY);
                        List<ContractOrder> asksOld = mapPlate.get(ContractOrderDirection.SELL);
                        coinTrader.cancelRobotOrder(bidsOld, ContractOrderDirection.BUY);
                        coinTrader.cancelRobotOrder(asksOld, ContractOrderDirection.SELL);
                    }
//                ContractUtils.getContractCoinService().saveAndFlush(coin);
                }
            }
        }
    }


    /**
     * 获取 OKEX 交易所的行情
     *
     * @return
     */
    private Ticker getTickerByUrl(ContractCoin coin) throws IOException {
        String symbol = coin.getSymbol();
        List<String> listHuobi = new ArrayList<>();
        listHuobi.add("BTCUSDT");
        listHuobi.add("ETHUSDT");
        listHuobi.add("EOSUSDT");
        listHuobi.add("LTCUSDT");
        listHuobi.add("BSVUSDT");

        List<String> listBitMax = new ArrayList<>();
        listBitMax.add("HTUSDT");
        listBitMax.add("OKBUSDT");
        listBitMax.add("BNBUSDT");

        JSONObject tick = null;
        Ticker ticker = null;
        JSONObject depth = null;
        List<ContractOrder> bids = new ArrayList<>();
        List<ContractOrder> asks = new ArrayList<>();
        if (listBitMax.contains(symbol)) {
            String bitmaxStr = HttpsUtil.doGet("https://bitmax.io/api/pro/v1/ticker?symbol=" + coin.getCoinSymbol() + "/" + coin.getBaseSymbol());
            String bnbDepthStr = HttpsUtil.doGet("https://bitmax.io/api/pro/v1/depth?symbol=" + coin.getCoinSymbol() + "/" + coin.getBaseSymbol());
//            String bnbDepthStr = readJsonFile("J:\\source\\bnbDepth.txt");
//            String bitmaxStr = readJsonFile("J:\\source\\bitMax.txt");
            JSONObject forObject = JSON.parseObject(bitmaxStr);
            if (forObject.getInteger("code") == 0) {
                JSONObject json = forObject.getJSONObject("data");
                ticker = new Ticker();
                ticker.setSymbol(coin.getSymbol());
                ticker.setTradePrice(json.getBigDecimal("close"));
                ticker.setTradeSize(json.getBigDecimal("volume").divide(coin.getMinVolume()).setScale(0, BigDecimal.ROUND_DOWN));
            } else {
                return null;
            }
            //添加盘口深度
            depth = JSON.parseObject(bnbDepthStr);
            if (depth.containsKey("code") && "0".equals(depth.getString("code"))) {
                tick = depth.getJSONObject("data").getJSONObject("data");
            }
        }
        if (!listBitMax.contains(symbol)) {
            String huobiStr = HttpsUtil.doGet("https://api.btcgateway.pro/swap-ex/market/trade?contract_code=" + coin.getCoinSymbol() + "-USD");
            String depthStr = HttpsUtil.doGet("https://api.btcgateway.pro/swap-ex/market/depth?contract_code=" + coin.getCoinSymbol() + "-USD&type=step6");
//            String huobiStr = readJsonFile("J:\\source\\huobiUSD.txt");
//            String depthStr = readJsonFile("J:\\source\\testRobot.txt");
            JSONObject jsonObject = JSON.parseObject(huobiStr);
            if (jsonObject.containsKey("status") && jsonObject.getString("status").equals("ok")) {
                List<JSONObject> objs = JSON.parseArray(jsonObject.getJSONObject("tick").getString("data"), JSONObject.class);
                JSONObject json = objs.get(0);
                ticker = new Ticker();
                ticker.setSymbol(coin.getSymbol());
                ticker.setTradePrice(json.getBigDecimal("price"));
                ticker.setTradeSize(json.getBigDecimal("amount"));
            } else {
                return null;
            }
            //添加盘口深度
            depth = JSON.parseObject(depthStr);
            if (depth.containsKey("status") && "ok".equals(depth.getString("status"))) {
                tick = depth.getJSONObject("tick");
            }
        }

        List<JSONArray> bidsArray = JSON.parseArray(tick.getString("bids"), JSONArray.class);
        List<JSONArray> asksArray = JSON.parseArray(tick.getString("asks"), JSONArray.class);
        if (bidsArray.size() > 20) {
            bidsArray = bidsArray.subList(0, 20);
        }
        if (asksArray.size() > 20) {
            asksArray = asksArray.subList(0, 20);
        }
        bidsArray.stream().forEach(bid -> {
            ContractOrder order = new ContractOrder();
            order.setPrice(new BigDecimal(bid.get(0).toString()));
            order.setAmount(new BigDecimal(bid.get(1).toString()).multiply(coin.getMinVolume()));
            order.setNowp(new BigDecimal(bid.get(1).toString()));
            order.setDirection(ContractOrderDirection.BUY);
            order.setType(ContractOrderType.LIMIT_PRICE);
            order.setTradedAmount(BigDecimal.ZERO);
            bids.add(order);
        });
        asksArray.stream().forEach(ask -> {
            ContractOrder order = new ContractOrder();
            order.setPrice(new BigDecimal(ask.get(0).toString()));
            order.setAmount(new BigDecimal(ask.get(1).toString()).multiply(coin.getMinVolume()));
            order.setNowp(new BigDecimal(ask.get(1).toString()));
            order.setDirection(ContractOrderDirection.SELL);
            order.setType(ContractOrderType.LIMIT_PRICE);
            order.setTradedAmount(BigDecimal.ZERO);
            asks.add(order);
        });
        bids.sort(Comparator.comparing(ContractOrder::getPrice).reversed());
        asks.sort(Comparator.comparing(ContractOrder::getPrice));
        ticker.setAsks(asks);
        ticker.setBids(bids);
        return ticker;
    }


    @Override
    public void run() {
        robotAddOrder();
    }

    private ContractOrder initRobotContractOrder(ContractOrderDirection direction, Ticker ticker) {
        ContractOrder order = new ContractOrder();

        order.setBaseSymbol(coin.getBaseSymbol());
        order.setCoinSymbol(coin.getCoinSymbol());
        order.setSymbol(coin.getSymbol());
        order.setOrderResource(ContractOrderResource.PLAT);
        order.setType(ContractOrderType.LIMIT_PRICE);
        order.setDirection(direction);
        order.setMemberId(0L);

        //赋值binance最新价格
        order.setPositionFlag(ContractEnumType.ContractOrderPositionFlag.designate);

        ContractCoin bySymbol = coin;

        //挂单操作成交量范围控制
        //只有普通挂单才冻结相应保证金;
        //起始保证金

        if (direction == ContractOrderDirection.BUY) {
            order.setNowp(ticker.getBids().get(0).getNowp());
            order.setPrice(ticker.getBids().get(0).getPrice());
            order.setAmount(order.getNowp().multiply(coin.getMinVolume()));
            if (order.getPositionFlag() == ContractEnumType.ContractOrderPositionFlag.designate) {
                order.setPositionType(ContractOrderPositionType.RISE);
            }
        }
        if (direction == ContractOrderDirection.SELL) {
            order.setNowp(ticker.getBids().get(0).getNowp());
            order.setPrice(ticker.getAsks().get(0).getPrice());
            order.setAmount(order.getNowp().multiply(coin.getMinVolume()));
            if (order.getPositionFlag() == ContractEnumType.ContractOrderPositionFlag.designate) {
                order.setPositionType(ContractOrderPositionType.FALL);
            }
        }


        JSONObject map = ContractUtils.getStart(order.getPrice(), order.getAmount(), bySymbol, order.getNowp(), order.getMemberId(), null);
        if (map == null) {
            return null;
        }
        BigDecimal start = map.getBigDecimal("start");
        String startRate = map.getString("startRate");
        log.info("====================起始保证金:  " + start);

        //维持保证金
        JSONObject maintainMap = ContractUtils.getMaintain(order.getAmount(), bySymbol, order.getNowp(), order.getMemberId(), null);
        if (maintainMap == null) {
            return null;
        }
        BigDecimal maintain = maintainMap.getBigDecimal("maintain");
        String maintainRate = maintainMap.getString("maintainRate");
        log.info("====================维持保证金:  " + maintain);
        order.setStart(start);
        order.setStartRate(startRate);
        order.setMaintain(maintain);
        order.setMaintainRate(maintainRate);
        order.setPositionValue(BigDecimalUtils.mul(order.getAmount(), order.getPrice()));
        order.setTurnover(BigDecimal.ZERO);
        order.setTradedAmount(BigDecimal.ZERO);
        order.setSetTime(Calendar.getInstance().getTimeInMillis());
        order.setMultiple(new BigDecimal(100));

        return order;
    }

    public static String readJsonFile(String fileName) {
        String jsonStr = "";
        try {
            File jsonFile = new File(fileName);
            FileReader fileReader = new FileReader(jsonFile);
            Reader reader = new InputStreamReader(new FileInputStream(jsonFile), "utf-8");
            int ch = 0;
            StringBuffer sb = new StringBuffer();
            while ((ch = reader.read()) != -1) {
                sb.append((char) ch);
            }
            fileReader.close();
            reader.close();
            jsonStr = sb.toString();
            return jsonStr;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    class Ticker {
        private Long id;
        private String symbol; //交易对
        private BigDecimal tradePrice; //最新成交价格
        private BigDecimal tradeSize; //最新成交数量
        private ContractOrderDirection direction;
        private List<ContractOrder> bids;
        private List<ContractOrder> asks;

        public Long getId() {
            return id;
        }


        public void setId(Long id) {
            this.id = id;
        }

        public List<ContractOrder> getBids() {
            return bids;
        }

        public void setBids(List<ContractOrder> bids) {
            this.bids = bids;
        }

        public List<ContractOrder> getAsks() {
            return asks;
        }

        public void setAsks(List<ContractOrder> asks) {
            this.asks = asks;
        }

        public ContractOrderDirection getDirection() {
            return direction;
        }

        public void setDirection(ContractOrderDirection direction) {
            this.direction = direction;
        }

        public String getSymbol() {
            return symbol;
        }

        public void setSymbol(String symbol) {
            this.symbol = symbol;
        }

        public BigDecimal getTradePrice() {
            return tradePrice;
        }

        public void setTradePrice(BigDecimal tradePrice) {
            this.tradePrice = tradePrice;
        }

        public BigDecimal getTradeSize() {
            return tradeSize;
        }

        public void setTradeSize(BigDecimal tradeSize) {
            this.tradeSize = tradeSize;
        }

    }
}